﻿Imports System.Net.Sockets
Imports NLog

Public Class cBotConnection
    Inherits cConnectionBase

    Protected pSocket As Socket
    Protected pLSocket As Socket
    Protected pBuffer(&H7FFF - 1) As Byte

    WithEvents socketEvent1 As SocketAsyncEventArgs

    Protected Logger As NLog.Logger = LogManager.GetLogger("ToBotLogger")


    Sub New()
        pLSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP)
        pLSocket.ExclusiveAddressUse = False
    End Sub

    Public Overrides Sub Start(ByVal ip As String, ByVal port As UInt32)
        Set_Status(eSocketStatus.Accepting)
        Try
            If pLSocket.IsBound = False Then
                Dim ep As New System.Net.IPEndPoint(Net.IPAddress.Parse(ip), port)
                pLSocket.Bind(ep)
                pLSocket.Listen(2)
            End If
            Console.SetCursorPosition(0, 15)
            Console.WriteLine("Bot --> Accepting --> {0}:{1}", ip, port)
            If socketEvent1 IsNot Nothing Then socketEvent1.Dispose()
            socketEvent1 = New SocketAsyncEventArgs
            pLSocket.AcceptAsync(socketEvent1)
        Catch ex As Exception
            Set_Status(eSocketStatus.Offline)
            Console.WriteLine(ex.Message.ToString)
        End Try
    End Sub

    Public Overrides Sub Disconnect()
        If Status <> eSocketStatus.Offline Then
            Set_Status(eSocketStatus.Offline)
            Start("", 0)
            Console.SetCursorPosition(0, 15)
            Console.WriteLine("Bot --> Disconnect")
        End If
        Try
            pSocket.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
        Finally
            If pSocket IsNot Nothing Then
                pSocket.Close()
            End If
        End Try
    End Sub

    Public Overrides Sub Send(ByRef packet() As Byte)
        Try
            pSocket.Send(packet)
        Catch ex As Exception
            Console.SetCursorPosition(0, 15)
            Console.WriteLine("Bot send error " & ex.Message.ToString)
        End Try
    End Sub

    Private Sub socketEvent1_Completed(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs) Handles socketEvent1.Completed
        If e.SocketError = SocketError.Success Then

            Select Case e.LastOperation
                Case SocketAsyncOperation.Accept
                    Console.SetCursorPosition(0, 15)
                    Console.WriteLine("Bot --> Accepted")
                    pSocket = socketEvent1.AcceptSocket
                    socketEvent1.SetBuffer(pBuffer, 0, pBuffer.GetUpperBound(0) + 1)
                    pSocket.ReceiveAsync(socketEvent1)
                    Set_Status(eSocketStatus.Connected)

                Case SocketAsyncOperation.Receive
                    ProcessReceive(sender, e)
                Case Else
                    Console.WriteLine(e.LastOperation.ToString)
            End Select

        Else
            Console.WriteLine(e.LastOperation.ToString & "  " & e.SocketError.ToString)
            Disconnect()
        End If

    End Sub

    Private Sub ProcessReceive(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
        If e.BytesTransferred = 0 Then
            Disconnect()
            Exit Sub
        End If

        Dim len As Int32 = -1

        Dim byteCounter As Int32 = e.BytesTransferred + e.Offset

        If e.Offset <> 0 Then
            socketEvent1.SetBuffer(0, pBuffer.GetUpperBound(0) + 1)
        End If

        Using r As New IO.BinaryReader(New IO.MemoryStream(e.Buffer, 0, byteCounter))
            Do
                Dim PacketStartPos As UInt32 = r.BaseStream.Position

                len = -1

                If r.BaseStream.Position + 2 <= byteCounter Then 'can read Len ?
                    len = r.ReadUInt16()
                End If

                If r.BaseStream.Position + 4 + len <= byteCounter Then 'check if packet complett
                    r.BaseStream.Position = PacketStartPos
                    Dim packet As Byte() = r.ReadBytes(len + 6)
                    Add_Packet(packet)
                Else
                    len = -1
                End If

                If len = -1 Then 'no complett packet  --> write back to buffer
                    r.BaseStream.Position = PacketStartPos
                    Using w As New IO.BinaryWriter(New IO.MemoryStream(pBuffer))
                        w.Write(r.ReadBytes(r.BaseStream.Length - PacketStartPos))
                        w.Flush()
                        socketEvent1.SetBuffer(pBuffer, r.BaseStream.Length - PacketStartPos, pBuffer.GetUpperBound(0) - (r.BaseStream.Length - PacketStartPos) + 1)
                    End Using
                End If
            Loop Until r.BaseStream.Position = r.BaseStream.Length
        End Using

        Try
            pSocket.ReceiveAsync(socketEvent1)
        Catch ex As Exception
            Disconnect()
        End Try
    End Sub
End Class
